/*
 * Middleware
 * Copyright (C) 2010/11
 * Riccardo Pulvirenti, Giuseppe Ravidà, Andrea Tino
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANDABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses
 */

/*!
 * File: ui.cpp
 */

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>

#include "ui.hpp"
#include "wfqueue_proxy_factory.hpp"
#include "data_proxy_factory.hpp"
#include "task_descriptor.hpp"
#include "utils.hpp"
#include "data.hpp"

using namespace middleware;

//-----------------------------------------------------------------------------
// Constructors and destructor
/* Default constructor */
UI::UI() {
	this->_config.ptrto_manager = 0;
	this->_config.log_postfix = "";
	this->_config.ui_ip_toui_bind = "127.0.0.1";
	this->_config.ui_port_toui_bind = "0";
	// Logger
	std::string logfile = std::string("logfile_ui_");
	this->_logger.set_log_filename(logfile);
	this->_logger.set_log_tag("UI");
}
/* Constructor with configuration */
UI::UI(UIConfig conf) {
	this->_config.ptrto_manager = conf.ptrto_manager;
	this->_config.log_postfix = conf.log_postfix;
	this->_config.ui_ip_toui_bind = conf.ui_ip_toui_bind;
	this->_config.ui_port_toui_bind = conf.ui_port_toui_bind;
	// Logger
	std::string logfile = std::string("logfile_ui_") + conf.log_postfix;
	this->_logger.set_log_filename(logfile);
	this->_logger.set_log_tag("UI");
}
/* Destructor */
UI::~UI() {

}
//-----------------------------------------------------------------------------
// Private members
/* Sending workflow */
Workflow* UI::CreateWorkflow() {
	// Generate a random taskDescriptor set
	int ntask;
	std::vector<TaskDescriptor> tasks;
	Workflow* wf = new Workflow;
	TaskLinkage link;
	ntask = utils::GetRandomIntegralNumberInt(1, kUIMaxTasksInWorkflow);
	for (int i = 0; i < ntask; i++) {
		std::ostringstream stask;
		TaskDescriptor task;
		stask << "Task-" << i;
		//task.set_type((i % 2) == 0 ? kTaskExecutable : kTaskBashCommand);
		task.set_type(kTaskExecutable);
		//task.set_type(kTaskBashCommand);
		task.set_command("command_" + stask.str());
		link.machine_addr = "/home";
		link.network_inet_ip = this->_config.ui_ip_toui_bind;
		link.network_inet_port = this->_config.ui_port_toui_bind;
		task.set_link(link);
		tasks.push_back(task); /* Inserting task */
	}
	wf->assign(tasks.begin(), tasks.end()); /* Assigning tasks */
	// Return workflow
	return wf;
}
/* Thread execution flow */
void UI::exec() {
	std::cout << kUIEtq << "Running!" << std::endl;
	try {
		this->bonjour(); /* Initialization sequence */
		// Launching data request manager
		std::cout << kUIEtq << "Launching data request manager thread..." <<
				std::endl;
		this->_datareq_manager_thread = boost::shared_ptr<boost::thread>(
				new boost::thread(boost::bind(&UI::exec_datareq_manager,
						this)));
		// Creating proxy to communicate to Manager
		std::cout << kUIEtq << "Configuring proxy factory..." << std::endl;
		WFQueueProxyFactory proxy_factory(this->_config.ptrto_manager);
		std::cout << kUIEtq <<
				"Proxy factory configured: Pointer to Manager ok" <<
				std::endl;
		std::cout << kUIEtq << "Getting proxy from factory..." << std::endl;
		// Dynamic allocating proxy
		WFQueueManagerProxy* proxy = proxy_factory.getProxy(); /* Got proxy */
		std::cout << kUIEtq << "Got proxy!" << std::endl;
		std::cout << kUIEtq << "Entering workflow random generation loop..." <<
				std::endl;
		// Sending workflows randomly
		for (;;) {
			unsigned int waiter = (utils::GetRandomIntegralNumberInt(1,
					kUIMaxWorkflowGeneretionPeriod) * 1000);
			Workflow* wf = this->CreateWorkflow();
			std::cout << kUIEtq << "Created WF: " << wf->get_id() <<
					" having " << wf->size() << " tasks!" << std::endl;
			// Send workflow
			proxy->enqueue(*wf);
			std::cout << kUIEtq << "Submitted WF: " << wf->get_id() << " (" <<
					wf->size() << " tasks)!" << std::endl;
			this->_logger.Print(std::string("Submitted workflow WID=") +
					boost::lexical_cast<std::string>(wf->get_id()) +
					std::string(" having ") +
					boost::lexical_cast<std::string>(wf->size()) +
					std::string(" tasks to Manager."));
			delete wf; /* Clearing */
			std::cout << kUIEtq << "Reactivating loop after " << waiter <<
					" msecs..." << std::endl;
			// Sleeping a random interval
			boost::this_thread::sleep(boost::posix_time::milliseconds(waiter));
		}
	} catch(std::exception& e) {
		/* Proxy reference is automatically destroys as the factory gets
		 * out of scope */
		std::cout << kUIEtq << "Error: " << e.what() << std::endl;
		std::cout << kUIEtq << "Now exiting!" << std::endl;
	}
}
/* Data request management */
void UI::exec_datareq_manager() {
	std::cout << kUIEtq << kUI1Etq << "Now running!" << std::endl;
	/* Listening to assigned port for incoming data (dummy data) and provide
	 * to requestor, requested data */
	Data data(std::string("Autogenerated data"), 1000);
	try {
		std::cout << kUIEtq << kUI1Etq <<
				"Configuring factory and getting proxy..." << std::endl;
		DataProxyFactory proxy_factory(kAcceptReqAndSendDataProxy,
				this->_config.ui_ip_toui_bind, this->_config.ui_port_toui_bind);
		DataManagerProxy* proxy = proxy_factory.getProxy();
		std::cout << kUIEtq << kUI1Etq << "Got proxy!" << std::endl;
		std::cout << kUIEtq << kUI1Etq <<
				"Entering data request lintener loop..." << std::endl;
		for (;;) {
			std::cout << kUIEtq << kUI1Etq <<
					"Waiting for data request..." << std::endl;
			try {
				proxy->accept_req_send_data(data);
				std::cout << kUIEtq << kUI1Etq <<
						"A data request has been SERVED!" << std::endl;
			} catch(std::exception& e) {
				std::cout << kUIEtq << kUI1Etq <<
						"A data request COULD NOT BE SERVED!" << std::endl;
			}
		}
	} catch(std::exception& e) {
		/* Proxy reference is automatically destroys as the factory gets
				 * out of scope */
		std::cout << kUIEtq << kUI1Etq << "Error: " << e.what() << std::endl;
		std::cout << kUIEtq << kUI1Etq << "Now exiting!" << std::endl;
	}
}
/* Bonjour */
void UI::bonjour() throw(GenericException) {
	std::cout << kUIEtq << "Initialization sequence now started..." <<
			std::endl;
	std::cout << kUIEtq << "Configuration loaded: Pointer to Manager ok" <<
			"; bindIP=" << this->_config.ui_ip_toui_bind << ", bindPORT=" <<
			this->_config.ui_port_toui_bind << std::endl;
	if (this->_config.ptrto_manager == 0)
		throw GenericException("Invalid parameters provided!");
	std::cout << kUIEtq << "Initialization completed!" << std::endl;
}
